package org.jetbrains.plugins.scala.compiler.highlighting

import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.vfs.AsyncFileListener
import com.intellij.openapi.vfs.newvfs.events.{VFileDeleteEvent, VFileEvent}
import com.intellij.problems.WolfTheProblemSolver
import com.intellij.util.concurrency.annotations.RequiresReadLock
import org.jetbrains.plugins.scala.compiler.highlighting.BackgroundExecutorService.executeOnBackgroundThreadInNotDisposed
import org.jetbrains.plugins.scala.settings.ScalaHighlightingMode

import scala.jdk.CollectionConverters._

private final class CompilerHighlightingFileListener extends AsyncFileListener {

  @RequiresReadLock
  override def prepareChange(events: java.util.List[_ <: VFileEvent]): AsyncFileListener.ChangeApplier = {
    val deletedFiles = events.asScala.collect { case event: VFileDeleteEvent => event.getFile }.toSeq

    if (deletedFiles.isEmpty) return null

    val deletedFilesPerOpenProject = ProjectManager.getInstance().getOpenProjects.toSeq.flatMap { project =>
      // The prepareChange method needs to quickly react to cancellation requests.
      // Check cancellation before filtering files for the next open project.
      ProgressManager.checkCanceled()
      val fileIndex = ProjectRootManager.getInstance(project).getFileIndex
      val files = deletedFiles.filter { f =>
        // Check cancellation before looking into the file indices of the open project.
        ProgressManager.checkCanceled()
        fileIndex.isInSourceContent(f)
      }
      if (files.nonEmpty) Some(project -> files) else None
    }

    if (deletedFilesPerOpenProject.isEmpty) return null

    new AsyncFileListener.ChangeApplier {
      override def afterVfsChange(): Unit = {
        deletedFilesPerOpenProject.foreach { case (project, files) =>
          executeOnBackgroundThreadInNotDisposed(project) {
            if (ScalaHighlightingMode.isShowErrorsFromCompilerEnabled(project)) {
              // Remove errors generated by Compiler Based Highlighting that have previously been reported to the Wolf
              // for all removed files. This must be done on a background thread.
              val wolf = WolfTheProblemSolver.getInstance(project)
              val source = ExternalHighlightersService.instance(project)
              files.foreach(wolf.clearProblemsFromExternalSource(_, source))
            }
          }
        }
      }
    }
  }
}
